package cn.dansj.common.request.advisor;

import cn.dansj.common.utils.enums.BooleanType;
import cn.dansj.common.utils.spring.advisor.AbstractBeanMethodFactoryPointcutAdvisor;
import cn.dansj.common.utils.transfer.ArrayUtils;
import cn.dansj.common.utils.transfer.AssertUtils;
import cn.dansj.common.utils.transfer.Transformation;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.annotation.AnnotatedElementUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public abstract class AbstractAnnotationPointerAdvisor<T extends Annotation> extends AbstractBeanMethodFactoryPointcutAdvisor {
    private final List<?> booleanClass = ArrayUtils.asList(BooleanType.class, boolean.class, Boolean.class);
    private final ConcurrentMap<String, T> concurrentMap = new ConcurrentHashMap<>();

    @SuppressWarnings("unchecked")
    public Class<T> getAnnotationClass() {
        try {
            return (Class<T>) ((ParameterizedType) AopUtils.getTargetClass(this).getSuperclass().getGenericSuperclass()).getActualTypeArguments()[0];
        } catch (Exception exception) {
            return (Class<T>) ((ParameterizedType) AopUtils.getTargetClass(this).getGenericSuperclass()).getActualTypeArguments()[0];
        }
    }

    @Override
    public boolean match(Method method, Class<?> targetClass) {
        final T annotation = AnnotatedElementUtils.findMergedAnnotation(method, getAnnotationClass());

        if (annotation == null) {
            return false;
        }

        try {
            final Method value = annotation.annotationType().getMethod("value");
            if (booleanClass.contains(value.getReturnType())) {
                final boolean castToBoolean = Transformation.castToBoolean(value.invoke(annotation));
                if (castToBoolean) saveMethodAnnotation(method, annotation);
                return castToBoolean;
            } else {
                saveMethodAnnotation(method, annotation);
                return true;
            }
        } catch (Exception exception) {
            saveMethodAnnotation(method, annotation);
            return true;
        }
    }

    private void saveMethodAnnotation(Method method, T annotation) {
        final String key = method.getDeclaringClass().getName() + "." + method.getName();
        AssertUtils.state(!(concurrentMap.containsKey(key) && !concurrentMap.get(key).equals(annotation)), key + "已存在,添加失败");
        concurrentMap.put(key, annotation);
    }

    public T getGenericAnnotation(Method method) {
        return AnnotatedElementUtils.findMergedAnnotation(method, getAnnotationClass());
    }

    public <U extends Annotation> U getAnnotation(Method method, Class<U> u) {
        return AnnotatedElementUtils.findMergedAnnotation(method, u);
    }
}
